Skip to content

CI: add SwiftPM iOS e2e workflows (RNTester, HelloWorld, new app) - #57659

Open
chrfalch wants to merge 11 commits into
mainfrom
chrfalch/add-e2e-tests-for-swiftpm
Open

CI: add SwiftPM iOS e2e workflows (RNTester, HelloWorld, new app)#57659
chrfalch wants to merge 11 commits into
mainfrom
chrfalch/add-e2e-tests-for-swiftpm

Conversation

@chrfalch

Copy link
Copy Markdown
Collaborator

Summary:

Adds CI coverage for the SwiftPM (SPM) iOS path, which currently has none. Three standalone workflows scaffold + convert an app to SPM using the prebuilt XCFrameworks and compile it for the iOS simulator:

  • test-ios-spm-rntester.ymlpackages/rn-tester
  • test-ios-spm-helloworld.ymlprivate/helloworld
  • test-ios-spm-newapp.yml — a fresh copy of private/helloworld, wired to a Verdaccio-published build of this monorepo

None of them use @react-native-community/cli. The shared logic lives in two committed, locally-runnable scripts (scripts/e2e/spm-prime-artifacts.js, scripts/e2e/spm-ios-e2e.js); the workflows are thin wrappers. They run manually (workflow_dispatch) and nightly (schedule).

This also adds an optional slices input to prebuild-ios-dependencies.yml and prebuild-ios-core.yml. It defaults to the full platform set, so existing callers (test-all.yml, releases) are unchanged; the e2e workflows request only ios-simulator, since that is all they compile against.

Changelog:

[INTERNAL] [ADDED] - CI workflows validating the SwiftPM iOS build (RNTester, HelloWorld, new app)

Test Plan:

  • Trialed the HelloWorld workflow on this branch (temporary push trigger, since removed): full run green for both Debug and Release, incl. the real spm add + xcodebuild. The slices input correctly collapsed the prebuild matrices to the single ios-simulator slice per flavor.
  • node --check, prettier --check, and eslint clean on both scripts; all workflow YAML validated.

chrfalch and others added 2 commits July 24, 2026 08:53
Adds three standalone workflows that scaffold + convert an iOS app to
SwiftPM using the prebuilt XCFrameworks and compile it:

  - test-ios-spm-rntester.yml   (packages/rn-tester)
  - test-ios-spm-helloworld.yml (private/helloworld)
  - test-ios-spm-newapp.yml     (fresh copy of helloworld, wired to a
                                 Verdaccio-published local monorepo build)

None of these use @react-native-community/cli. Each workflow builds the
prebuilt dependencies + core XCFrameworks via the existing reusable
prebuild-ios-* workflows, primes both flavor slots, then runs the shared
logic in two committed, locally-runnable scripts:

  - scripts/e2e/spm-prime-artifacts.js  (lay CI tarballs + hermes into the
                                         --artifacts <dir>/<flavor>/ layout)
  - scripts/e2e/spm-ios-e2e.js          (scaffold -> spm add --deintegrate
                                         -> xcodebuild, per app)

Triggered manually (workflow_dispatch) and nightly (schedule).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an optional `slices` input to prebuild-ios-dependencies.yml and
prebuild-ios-core.yml, driving their build matrix from it. It defaults
to the full platform set, so existing callers (test-all.yml, releases)
are unchanged.

The three SwiftPM iOS e2e workflows only ever compile for the iOS
simulator, so they now request `["ios-simulator"]` from both prebuild
workflows — cutting the dependencies matrix from 8 slices to 1 and the
core matrix from 3 to 1 (per flavor), which is the dominant cost of
those e2e runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 24, 2026
@chrfalch
chrfalch requested a review from cipolleschi July 24, 2026 07:41
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 24, 2026
chrfalch and others added 2 commits July 24, 2026 10:11
- spm-ios-e2e.js: build the execFileSync `env` from process.env's string
  entries only (process.env is Flow-typed to allow numbers, which the
  execFileSync `env` option rejects), and omit `env` entirely when there
  is no override so the child inherits process.env as-is.
- Both scripts: suppress the parseArgs [incompatible-type] error with the
  standard "Natural Inference rollout" $FlowFixMe, matching set-version.js
  and init-project-e2e.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
execFileSync's `env` option is invariantly typed {[string]: string |
number | void}, so neither a plain process.env spread ({[string]: string
| void}) nor a string-only object satisfies it. Build the env object
into a correctly-typed indexer instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cipolleschi cipolleschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for hardening our CI. However, I think that we are not quite there. This approach does not follow the patterns we are currently using in CI. I left a few comments to improve this approach.

Comment thread .github/workflows/test-ios-spm-helloworld.yml Outdated
Comment thread .github/workflows/test-ios-spm-helloworld.yml
Comment thread .github/workflows/test-ios-spm-newapp.yml Outdated
Comment thread .github/workflows/test-ios-spm-newapp.yml Outdated
Comment thread .github/workflows/test-ios-spm-helloworld.yml
Comment thread .github/workflows/test-ios-spm-newapp.yml
Comment thread .github/workflows/test-ios-spm-newapp.yml
Comment thread .github/workflows/test-ios-spm-rntester.yml
Comment thread scripts/e2e/spm-ios-e2e.js Outdated
Comment thread scripts/e2e/spm-prime-artifacts.js Outdated
Addresses review feedback that the original approach did not follow the
patterns already used in CI:

- The three workflows no longer trigger prebuild-ios-dependencies /
  prebuild-ios-core themselves. They are workflow_call-only and consume
  the ReactCore / ReactNativeDependencies artifacts produced elsewhere in
  the run, the way e2e-ios-templateapp does. The `slices` input added to
  the two prebuild workflows is reverted.
- They are invoked from test-all.yml instead of carrying their own cron.
  Note this means they run per-PR (gated on should_test_ios) and on
  pushes to main, not nightly -- test-all.yml has no schedule trigger.
  They start as continue-on-error so a brand-new lane cannot hard-block
  unrelated iOS PRs; remove that once it has proven green on main.
- The new-app workflow reuses the react-native-package artifact instead
  of rebuilding the npm package.
- Both helper scripts are deleted. Priming the artifact slots needs no
  new code: download-spm-artifacts.js already accepts tarball overrides
  via RN_CORE_TARBALL_PATH / RN_DEPS_TARBALL_PATH, so the workflows call
  `npx react-native spm download` directly, and the new-app case reuses
  scripts/e2e/init-project-e2e.js --useHelloWorld.

Debug and Release run as parallel matrix cells. Each cell stages both
flavor slots because `spm add` validates both (it stages both framework
trees and selects one via a per-configuration build setting).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chrfalch

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — all five points addressed.

1. Don't re-run the prebuilds. Done. The workflows are workflow_call-only and download ReactCore{Debug,Release} + ReactNativeDependencies{Debug,Release} from the run, following e2e-ios-templateapp. The slices input I'd added to the two prebuild workflows is reverted (zero diff vs main).

2. Call it from test-all, no cron. Done — three reusable calls modeled on test_e2e_ios_templateapp, gated on should_test_ios. One flag: test-all.yml has no schedule trigger (only dispatch / PR / push to main), so these now run per-PR and on main pushes rather than nightly — more coverage than the cron I removed, matching test_ios_helloworld / test_ios_rntester. Since this lane has never run before, I've left it continue-on-error: true so it can't hard-block unrelated iOS PRs; happy to flip that off once it's proven green, or to move it somewhere cheaper if 6 macOS jobs per iOS PR is too much.

3. Don't rebuild the npm package. Done. newapp needs: build_npm_package and downloads react-native-package, then uses the existing scripts/e2e/init-project-e2e.js --useHelloWorld — the same invocation test-all already uses for Android.

4. The two scripts. Both deleted. Priming needed no new code: download-spm-artifacts.js already accepts tarball overrides via RN_CORE_TARBALL_PATH / RN_DEPS_TARBALL_PATH, so it's an inline npx react-native spm download --artifacts <dir>. Net −650 lines vs the previous revision.

5. Debug/Release in parallel. They are parallel matrix cells. Each cell does still stage both flavors: ensureBothArtifactFlavors (setup-apple-spm.js:555-576, called from the add pipeline at ~:1102) validates both the debug/ and release/ slots unconditionally, and --download skip throws on an incomplete one — by design, since spm add stages both flavor framework trees and a per-configuration build setting selects one at build time. A true single-flavor cell would need a flavor-scoping flag in setup-apple-spm.js; glad to do that as a follow-up if you want the downloads halved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
meta-codesync Bot pushed a commit that referenced this pull request Jul 27, 2026
…gins.h (#57697)

Summary:
rn-tester's `MyNativeView` example imports `RCTFabricComponentsPlugins.h`. The quoted form resolves under CocoaPods (the bare filename is re-vended via `FACADE_REEXPOSED_HEADERS` in `rncore_facades.rb`) and under the internal Meta build (the per-target header generated by buck's `plugins_header`), but not under SwiftPM. Under SwiftPM the header is exposed as an include directory, so only `<React/RCTFabricComponentsPlugins.h>` resolves. The quoted import makes `MyNativeView` fail to compile when rn-tester is converted to SwiftPM (surfaced by the new SPM CI lane in #57659), and switching to the angle form alone would instead break the internal build.

The fix uses a `__has_include` conditional: the angle form when it is available (SwiftPM and CocoaPods) with the quoted form as a fallback (the internal build). This matches the existing pattern in rn-tester's own `NativeCxxModuleExample.h` and keeps all three build stacks compiling.

## Changelog:

[INTERNAL] [FIXED] - Make rn-tester's NativeComponentExample header import resolve under SwiftPM, CocoaPods and the internal build

Pull Request resolved: #57697

Test Plan:
- Repro on clean `main`: converting rn-tester to SwiftPM (`spm add --deintegrate`) and building fails at `RNTMyNativeViewComponentView.mm` with `fatal error: 'RCTFabricComponentsPlugins.h' file not found`.
- With this change: the same SwiftPM conversion plus `xcodebuild -configuration Debug -sdk iphonesimulator` gives `BUILD SUCCEEDED`.
- Internal build: `buck2 build fbsource//xplat/js/react-native-github/packages/rn-tester:NativeComponentExampleApple` succeeds (it takes the quoted fallback).
- Existing CocoaPods CI (`test_ios_rntester`) covers the CocoaPods path.

Reviewed By: cipolleschi

Differential Revision: D113771766

Pulled By: fabriziocucci

fbshipit-source-id: e4549508f8e56942b45fa128b2391a290499d4a8
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread .github/workflows/test-ios-spm-helloworld.yml
Comment thread .github/workflows/test-ios-spm-helloworld.yml Outdated
Comment thread .github/workflows/test-ios-spm-helloworld.yml Outdated
chrfalch and others added 3 commits July 28, 2026 18:18
react-native-xcode.sh already handles this: when there is no Pods
installation and the Pods hermesc path does not exist, it resolves the
hermes-compiler package through react-native's own dependency graph
(scripts/react-native-xcode.sh:83-99). Exporting HERMES_CLI_PATH from
the workflow bypassed that shipped resolution, so the Release cells were
not exercising what a real SwiftPM consumer does — and a harness-side
export of this variable has previously masked hermes VM/compiler
bytecode skew.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Prime SwiftPM artifacts" borrowed jargon from a local test harness and
made the step look bespoke. It is the ordinary `spm download`, pointed at
the XCFrameworks the prebuild jobs just produced instead of the published
nightly. Name it after what it does and say so in the comment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chrfalch
chrfalch requested a review from cipolleschi July 28, 2026 17:28
All six cells (rn-tester, HelloWorld and a fresh app, Debug and Release)
passed in run 30379384838, so the lanes no longer need to be advisory.
Dropping continue-on-error means a SwiftPM regression fails test-all
instead of being reported as a green run with red cells inside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants